package com.example.mylearn.juc.example;


/**
 * 三个线程交替打印a,b,c (synchronized)
 *
 *
 * 有个问题，如果A线程的执行时间超过了C的唤醒时间，那就会导致打印出ACB
 * 打印A：因为pa一开始锁住c,a，打印A后阻塞到pc启动。10毫秒后pb获取a锁，发现pa还没释放a锁。然后10毫秒后pc开始执行，发现b锁还没人获取（这里就是关键，由于pa还没释放a，导致pb获取不到a，也就不会去获取b了）
 * 打印C：这个时候，b被pc获取了。等到pa释放了a之后，随后释放c，此时pb线程终于可以获取到a了，但是很遗憾，b已经被pc先获取了。然后pa释放了c，此时刚好pc需要c，随后就先行打印除C了。
 * 打印B：
 *
 */
public class PrintABCBySync {




        public static class ThreadPrinter implements Runnable {
            private String name;
            private Object prev;
            private Object self;

            private ThreadPrinter(String name, Object prev, Object self) {
                this.name = name;
                this.prev = prev;
                this.self = self;
            }

            @Override
            public void run() {
                int count = 10;
                while (count > 0) {// 多线程并发，不能用if，必须使用whil循环
                    synchronized (prev) { // 先获取 prev 锁
                        synchronized (self) {// 再获取 self 锁
                            System.out.print(name);//打印

//                            if (name.equals("A")) {
//                                try {
//                                    Thread.sleep(50);
//                                } catch (InterruptedException e) {
//                                    throw new RuntimeException(e);
//                                }
//                            }
                            count--;

                            self.notifyAll();// 唤醒其他线程竞争self锁，注意此时self锁并未立即释放。
                        }
                        //此时执行完self的同步块，这时self锁才释放。
                        try {
                            prev.wait(); // 立即释放 prev锁，当前线程休眠，等待唤醒
                            /**
                             * JVM会在wait()对象锁的线程中随机选取一线程，赋予其对象锁，唤醒线程，继续执行。
                             */
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

        public static void main(String[] args) throws Exception {
            Object a = new Object();
            Object b = new Object();
            Object c = new Object();
            ThreadPrinter pa = new ThreadPrinter("A", c, a);
            ThreadPrinter pb = new ThreadPrinter("B", a, b);
            ThreadPrinter pc = new ThreadPrinter("C", b, c);

            new Thread(pa).start();
            Thread.sleep(10);//保证初始ABC的启动顺序
            new Thread(pb).start();
            Thread.sleep(10);
            new Thread(pc).start();
            Thread.sleep(10);
        }







}
